//! app args using clap

use clap::ArgMatches;

#[derive(Debug)]
struct ArgParseError {
    message: String,
}

impl ArgParseError {
    fn new<T: Into<String>>(msg: T) -> Self {
        Self {
            message: msg.into(),
        }
    }
}

impl std::fmt::Display for ArgParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.message)
    }
}

impl std::error::Error for ArgParseError {}

const APP_NAME: &'static str = "gayboy";
const APP_VERSION: &'static str = "0.0.1";

// check build app args works, return ArgMatches
pub fn build_app_args() -> ArgMatches {
    let matches = clap::Command::new(APP_NAME)
        .version(APP_VERSION)
        .about("a gameboy colour emulator written in rust")
        // filename
        .arg(
            clap::Arg::new("filename")
                .help("Sets the rom file to load")
                .required(true),
        )
        // serial
        .arg(
            clap::Arg::new("serial")
                .help("Prints the data from the serial port to stdout")
                .short('s')
                .long("serial")
                .action(clap::ArgAction::SetTrue),
        )
        // printer
        .arg(
            clap::Arg::new("printer")
                .help("Emulates a gameboy printer")
                .short('p')
                .long("printer")
                .action(clap::ArgAction::SetTrue),
        )
        // classic
        .arg(
            clap::Arg::new("classic")
                .help("Forces the emulator to run in classic gameboy mode")
                .short('c')
                .long("classic")
                .action(clap::ArgAction::SetTrue),
        )
        // scale
        .arg(
            clap::Arg::new("scale")
                .help("Sets the scale of the interface. Default value: 2")
                .short('x')
                .long("scale")
                .value_parser(parse_scale_var),
        )
        // audio
        .arg(
            clap::Arg::new("audio")
                .help("Enables audio")
                .short('a')
                .long("audio")
                .action(clap::ArgAction::SetTrue),
        )
        // skip checksum
        .arg(
            clap::Arg::new("skip-checksum")
                .help("Skips verification of the cartridge checksum")
                .long("skip-checksum")
                .action(clap::ArgAction::SetTrue),
        )
        // test mode
        .arg(
            clap::Arg::new("test-mode")
                .help("Starts the emulator in a special test mode")
                .long("test-mode")
                .action(clap::ArgAction::SetTrue),
        )
        .get_matches();
    matches
}

fn parse_scale_var(arg: &str) -> Result<u32, ArgParseError> {
    match arg.parse::<u32>() {
        Err(e) => Err(ArgParseError::new(format!("Could not parse scale: {}", e))),
        Ok(s) if s < 1 => Err(ArgParseError::new("Scale must be at least 1")),
        Ok(s) if s > 8 => Err(ArgParseError::new("Scale may be at most 8")),
        Ok(s) => Ok(s),
    }
}
